package com.fhzn.boss.advice;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fhzn.boss.entity.WebResponse;
import com.fhzn.commons.toolkit.exception.BuzException;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.event.Level;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;


/**
 * @author fhzn
 */
@RequiredArgsConstructor
@Slf4j
@org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {

    private final ObjectMapper objectMapper;
    private final ParamValidationErrorResolver paramValidationErrorResolver;

    @ExceptionHandler({BuzException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> requestNotFoundError(HttpServletRequest request, BuzException ex) {
        error("buz exception", request, ex);
        return error(ex.getErrorCode(), ex.getMessage());
    }

    @ExceptionHandler({NoHandlerFoundException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> requestNotFoundError(HttpServletRequest request, Exception ex) {
        warn("request not found", request, ex);
        return error(404, "request not found");
    }

    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> methodNotAllowError(HttpServletRequest request, Exception ex) {
        warn("method not allow", request, ex);
        return error(405, "method not allow");
    }


    @ExceptionHandler({MissingServletRequestParameterException.class, ServletRequestBindingException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> paramError(HttpServletRequest request, Exception ex) {
        warn("param error", request, ex);
        return error(10001, "请求参数有误");
    }

    @ExceptionHandler({BindException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> paramValidError(HttpServletRequest request, BindException ex) {
        warn("param valid error", request, ex);
        String error = paramValidationErrorResolver.resolve(ex);
        return error(10001, error);
    }

    @ExceptionHandler({Throwable.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public WebResponse<Void> systemError(HttpServletRequest request, Exception ex) {
        error("system error", request, ex);
        return error();
    }

    private void warn(String error, HttpServletRequest request, Exception ex) {
        log(Level.WARN, error, request, ex);
    }

    private void error(String error, HttpServletRequest request, Exception ex) {
        log(Level.ERROR, error, request, ex);
    }

    public void log(Level level, String error, HttpServletRequest request, Exception ex) {
        try {
            ImmutableMap.Builder<String, Object> headerBuilder = ImmutableMap.builder();
            Enumeration<String> headerNames = request.getHeaderNames();

            while (headerNames.hasMoreElements()) {
                String key = headerNames.nextElement();
                headerBuilder.put(key, request.getHeader(key));
            }

            ImmutableMap<String, Object> headers = headerBuilder.build();
            ImmutableMap.Builder<String, Object> req = ImmutableMap.<String, Object>builder().put("uri", request.getRequestURI()).put("header", headers).put("host", request.getRemoteAddr());
            if (request.getCookies() != null) {
                req.put("cookie", request.getCookies());
            }

            ImmutableMap<String, Object> reqMap = req.build();
            String errMsg = error + " " + objectMapper.writeValueAsString(reqMap);
            if (level == Level.WARN) {
                log.warn(errMsg, ex);
                return;
            }
            log.error(errMsg, ex);
        } catch (Exception e) {
            log.error("error log failed, error:{}", error, e);
        }
    }

    private WebResponse<Void> error(int code, String error) {
        return WebResponse.error(code, error);
    }

    private WebResponse<Void> error() {
        return error(WebResponse.ERROR, "系统错误，请稍后再试或联系管理员");
    }
}

